/*
 * Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2007.
 *
 * Licensed under the Aduna BSD-style license.
 */

package edu.tju.commons.net.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.zip.GZIPInputStream;

public class HttpClientUtil {

	/**
	 * Encodes a string according to RFC 1738 : Uniform Resource locators (URL).
	 * According to this spec, any characters outside the range 0x20 - 0x7E must
	 * be escaped because they are not printable characters. Within the range a
	 * number of characters are deemed unsafe or are marked as reserved. In
	 * short: According to the spec only the alphanumerics and the special
	 * characters from <tt>$-_.+!*'(),</tt> can be left unencoded. To be safe, this method
	 * will encode all characters that are not alphanumerics.
	 */
	public static void formUrlEncode(String s, StringBuilder buf) {
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);

			// Only characters in the range 48 - 57 (numbers), 65 - 90 (upper
			// case letters), 97 - 122 (lower case letters) can be left
			// unencoded. The rest needs to be escaped.

			if (c == ' ') {
				// Replace all spaces with a '+'
				buf.append('+');
			}
			else {
				int cInt = (int)c;
				if (cInt >= 48 && cInt <= 57 || cInt >= 65 && cInt <= 90 || cInt >= 97 && cInt <= 122) {
					// alphanumeric character
					buf.append(c);
				}
				else {
					// Escape all non-alphanumerics
					buf.append('%');
					String hexVal = Integer.toHexString((int)c);

					// Ensure use of two characters
					if (hexVal.length() == 1) {
						buf.append('0');
					}

					buf.append(hexVal);
				}
			}
		}
	}

	/**
	 * Encodes a string according to RFC 1738 : Uniform Resource locators (URL).
	 * According to this spec, any characters outside the range 0x20 - 0x7E must
	 * be escaped because they are not printable characters. Within the range a
	 * number of characters are deemed unsafe or are marked as reserved. In
	 * short: According to the spec only the alphanumerics and the special
	 * characters from <tt>$-_.+!*'(),</tt> can be left unencoded. To be safe, this method
	 * will encode all characters that are not alphanumerics.
	 */
	public static String formUrlEncode(String s) {
		StringBuilder result = new StringBuilder(s.length() + 10);
		formUrlEncode(s, result);
		return result.toString();
	}

	/**
	 * Append a properly encoded parameter to the query string of the specified
	 * URL.
	 * 
	 * @param url
	 *        the URL to append to
	 * @param parameterName
	 *        the name of parameter to append
	 * @param parameterValue
	 *        the value to append
	 * @return the URL with the specified parameter appended to it
	 */
	public static final String appendParameter(String url, String parameterName, String parameterValue) {
		StringBuilder result = new StringBuilder(url);

		if (url.indexOf('?') >= 0) {
			result.append('&');
		}
		else {
			result.append('?');
		}

		result.append(encodeParameter(parameterName, parameterValue));

		return result.toString();
	}

	/**
	 * Encode a parameter for use in a URL.
	 * 
	 * @param parameterName
	 *        the name of the parameter
	 * @param parameterValue
	 *        the value
	 * @return the encoded parameter
	 */
	public static String encodeParameter(String parameterName, String parameterValue) {
		StringBuilder result = new StringBuilder();
		try {
			result.append(URLEncoder.encode(parameterName, "UTF-8"));
			result.append('=');
			result.append(URLEncoder.encode(parameterValue, "UTF-8"));
		}
		catch (UnsupportedEncodingException e) {
			// nop, UTF-8 is supported
		}

		return result.toString();
	}

	/**
	 * Sets a request property on the supplied connection indicating that a
	 * server can respond with gzip-encoded data if it wants to.
	 */
	public static void setAcceptGZIPEncoding(URLConnection conn) {
		conn.setRequestProperty(RequestHeaders.ACCEPT_ENCODING, "gzip");
	}

	/**
	 * Gets the InputStream for reading the response from a server. This method
	 * handles any encoding-related decoding of the data, e.g. gzip.
	 */
	public static InputStream getInputStream(URLConnection conn)
		throws IOException
	{
		InputStream responseStream = conn.getInputStream();

		String contentEncoding = conn.getContentEncoding();
		if ("gzip".equalsIgnoreCase(contentEncoding)) {
			responseStream = new GZIPInputStream(responseStream);
		}

		return responseStream;
	}

	/**
	 * Checks whether the specified status code is in the 2xx-range, indicating a
	 * successfull request.
	 * 
	 * @return <tt>true</tt> if the status code is in the 2xx range.
	 */
	public static boolean is2xx(int statusCode) {
		return statusCode >= 200 && statusCode < 300;
	}
}
